package org.dmcs.homework.statistics.implementations;

import java.util.Arrays;

import org.dmcs.homework.statistics.api.ICovarianceCalculator;
import org.dmcs.homework.statistics.api.IFischerCoefficientCalculator;
import org.dmcs.homework.statistics.api.IMeanCalculator;

import Jama.Matrix;

/**
 * A tool to calculate Fischer coefficient for a given data set, set of classes and considered dimensions.
 * @author Zielony
 * @version 1.0
 */
public class FischerCoefficientCalculator implements
		IFischerCoefficientCalculator {
	/**
	 * A tool for means calculation.
	 */
	private IMeanCalculator meanCalculator;
	
	/**
	 * A calculator for the covariance matrix.
	 */
	private ICovarianceCalculator covarianceCalculator;
	
	/**
	 * An util to deal with vector operations.
	 */
	private VectorUtil vectorUtil;
	
	/**
	 * A konstructor for DI.
	 * @param meanCalculator a calculator for the means.
	 * @param covarianceCalculator a calculator for the covariance matrix.
	 * @param vectorUtil an util to deal with vector operations.
	 */
	public FischerCoefficientCalculator(IMeanCalculator meanCalculator,
			ICovarianceCalculator covarianceCalculator, VectorUtil vectorUtil) {
		this.meanCalculator = meanCalculator;
		this.covarianceCalculator = covarianceCalculator;
		this.vectorUtil = vectorUtil;
	}
	
	/**
	 * {@inheritDoc}
	 */
	public double calculateFischerCoefficient(Matrix data, int[][] classes,
			int[] selectedDimensions) {
		/*
		 * Obliczenie centroidu dla wszystkich klas wspolnie.
		 */
		double[] globalAverage = meanCalculator.calculateMeanVector(data);
		
		/*
		 * Licznik we wzorze na wspolczynnik Fischera.
		 */
		double numerator = 0;
		
		/*
		 * Mianownik we wzorze na wspolczynnik Fischera.
		 */
		double denominator = 0;
		
		for(int i=0; i<classes.length; i++) {
			/*
			 * Obliczanie wektora rozrzutu miedzyklasowego w liczniku per-klasa i
			 * sumowanie wynikow, dla wszystkich wymiarów.
			 */
			numerator += vectorUtil.length(vectorUtil.subtract(meanCalculator.calculateMeanVector(data,classes[i]),
					globalAverage));
			
			/*
			 * Obliczanie wyznacznika macierzy kowariancji dla wybranych klas i 
			 * wybranych cech -> do mianownika.
			 */
			denominator += covarianceCalculator.calculateCovarianceMatrix(data,
					classes[i], selectedDimensions).det();
		}
		
		/*
		 * Poniewaz licznik moze byc tylko dodatki, to dla mianownika dazacego do zera, wspolczynnik
		 * Fischera dazy do nieskonczonosci -> separacja jest bardzo dobra.
		 */
		if(denominator == 0 ) {
			return Double.NEGATIVE_INFINITY;
		}
		
		/*
		 * Wspolczynnik Fischera.
		 */
		return numerator/denominator;
	}
}